unit uFrmCharacterCreation;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, ComCtrls, Buttons, uInNomineCharacter,
  uInNomineSystem;
  
type
  TfrmCharacterCreation = class(TForm)
    pcCharacterCreationSteps: TPageControl;
    tsConcept: TTabSheet;
    GroupBox1: TGroupBox;
    bBriefDescription: TBevel;
    lbBriefDescription: TLabel;
    cbCharacterConcept: TComboBox;
    btNext1: TBitBtn;
    tsForcesAndCharacteristics: TTabSheet;
    gbForces: TGroupBox;
    lbCorporeal: TLabel;
    lbEthereal: TLabel;
    lbCelestial: TLabel;
    rgForces: TRadioGroup;
    edCorporealForces: TEdit;
    edEtherealForces: TEdit;
    edCelestialForces: TEdit;
    gbCharacteristics: TGroupBox;
    lbAttribute1: TLabel;
    lbAttribute2: TLabel;
    edAttribute1: TEdit;
    edAttribute2: TEdit;
    btNext2: TBitBtn;
    tsAttunements: TTabSheet;
    tsSkills: TTabSheet;
    gbBasicDetails: TGroupBox;
    lbName: TLabel;
    edName: TEdit;
    lbKind: TLabel;
    cbCelestialKind: TComboBox;
    lbWord: TLabel;
    edWord: TEdit;
    lbSuperior: TLabel;
    gbAttunements: TGroupBox;
    tsArtifacts: TTabSheet;
    lbAttunements: TListBox;
    lbYourAttunements: TLabel;
    Hits: TGroupBox;
    edBodyHits: TEdit;
    edMindHits: TEdit;
    edSoulHits: TEdit;
    lbBodyHits: TLabel;
    lbMindHits: TLabel;
    lbSoulHits: TLabel;
    gbServitorAttunements: TGroupBox;
    lbSuperior2: TLabel;
    cbSuperior1: TComboBox;
    lbServitorAttunements: TLabel;
    cbServitorAttunements: TComboBox;
    btAddServitorAttunement: TBitBtn;
    btRemoveAttunement: TBitBtn;
    btNext3: TBitBtn;
    btNext5: TBitBtn;
    gbSkills: TGroupBox;
    lbSkills: TListBox;
    lbYourSkills: TLabel;
    gpAvailableSkills: TGroupBox;
    cbAvailableSkills: TComboBox;
    lbSkill: TLabel;
    lbSkillAttribute: TLabel;
    lbSkillDefault: TLabel;
    btSkillBuy: TBitBtn;
    lbSkillLevel: TLabel;
    edSkillLevel: TEdit;
    btSkillRemove: TBitBtn;
    cbSuperior: TComboBox;
    tbVesselAndServant: TTabSheet;
    gbVesselsAndServants: TGroupBox;
    lbYourVessels: TLabel;
    lbVessels: TListBox;
    gbVesselOrServant: TGroupBox;
    btVesselOrServantBuy: TBitBtn;
    tsSongs: TTabSheet;
    Label1: TLabel;
    lbServants: TListBox;
    btNext6: TBitBtn;
    edVesselOrServantDescription: TEdit;
    lbVesselOrServantDescription: TLabel;
    lbVesselOrServantLevel: TLabel;
    edVesselOrServantLevel: TEdit;
    rgVesselOrServant: TRadioGroup;
    cbServantClass: TComboBox;
    lbServantClass: TLabel;
    lbVesselCharismaLevel: TLabel;
    edVesselCharismaLevel: TEdit;
    btVesselOrServantRemove: TBitBtn;
    btNext4: TBitBtn;
    gbSongs: TGroupBox;
    lbYourSongs: TLabel;
    lbSongs: TListBox;
    gbAvailableSongs: TGroupBox;
    Label3: TLabel;
    lbSongLevel: TLabel;
    cbAvailableSongs: TComboBox;
    btSongBuy: TBitBtn;
    edSongLevel: TEdit;
    btSongRemove: TBitBtn;
    rgSongRealm: TRadioGroup;
    gbChoirAttunements: TGroupBox;
    btAddChoirAttunement: TBitBtn;
    cbChoirAttunements: TComboBox;
    lbSkillSpecification: TLabel;
    edSkillSpecification: TEdit;
    tsDiscords: TTabSheet;
    lbSkillAttribute2: TLabel;
    lbSkillDefault2: TLabel;
    GroupBox2: TGroupBox;
    BitBtn3: TBitBtn;
    GroupBox3: TGroupBox;
    BitBtn1: TBitBtn;
    lbVesselRoleLevel: TLabel;
    edVesselRoleLevel: TEdit;
    lbVesselStatus: TLabel;
    edVesselStatus: TEdit;
    lbMortalSuperior: TLabel;
    edMortalSuperior: TEdit;

    procedure FormCreate(Sender: TObject);
    procedure cbCharacterConceptSelect(Sender: TObject);
    procedure btNextClick(Sender: TObject);
    procedure cbCelestialKindChange(Sender: TObject);
    procedure edNameChange(Sender: TObject);
    procedure edWordChange(Sender: TObject);
    procedure tsConceptShow(Sender: TObject);
    procedure tsForcesAndCharacteristicsShow(Sender: TObject);
    procedure rgForcesClick(Sender: TObject);
    procedure edForcesChange(Sender: TObject);
    procedure edAttributeChange(Sender: TObject);
    procedure cbSuperiorChange(Sender: TObject);
    procedure tsAttunementsShow(Sender: TObject);
    procedure pcCharacterCreationStepsChanging(Sender: TObject; var AllowChange: Boolean);
    procedure cbSuperior1Change(Sender: TObject);
    procedure btAddChoirAttunementClick(Sender: TObject);
    procedure btAddServitorAttunementClick(Sender: TObject);
    procedure btRemoveAttunementClick(Sender: TObject);
    procedure tsSongsShow(Sender: TObject);
    procedure rgSongRealmClick(Sender: TObject);
    procedure btSongBuyClick(Sender: TObject);
    procedure btSongRemoveClick(Sender: TObject);
    procedure tsSkillsShow(Sender: TObject);
    procedure cbAvailableSkillsChange(Sender: TObject);
    procedure btSkillBuyClick(Sender: TObject);
    procedure cbAvailableSongsChange(Sender: TObject);
    procedure btSkillRemoveClick(Sender: TObject);
    procedure tbVesselAndServantShow(Sender: TObject);
    procedure rgVesselOrServantClick(Sender: TObject);
    procedure btVesselOrServantBuyClick(Sender: TObject);
    procedure btVesselOrServantRemoveClick(Sender: TObject);
    procedure edMortalSuperiorChange(Sender: TObject);
  private
    function GetMortalVessel: TVessel;
    function GetTotalForcesPointsToSpend: integer;
    function GetTotalCharacteristicsPointsToSpend(forcesType: TForcesType): integer;
    function GetForcesPointsToSpend: integer;
    function GetCharacteristicsPointsToSpend(forcesType: TForcesType): integer;
    function GetResoucePointsToSpend: integer;
    function HasThatAttunement(attunement: TAttunement): boolean;
    function HasThatSong(song: TSong): boolean;
    function HasThatSkill(skill: TSkill): boolean;
    function HasThatVessel(vessel: TVessel): boolean;
    function HasThatServant(servant: TServant): boolean;
    function GetSongFromYourResources(songDescription: String; songRealm: TRealm): TSong;
    function GetSkillFromYourResources(skillDescription: String; skillSpecification: String = ''): TSkill;
    function GetVesselFromYourResources(vesselDescription: String): TVessel;
    function GetServantFromYourResources(servantDescription: String; servantClass: TServantClass): TServant;

    procedure AddFreeChoirAttunement(superior: TSuperior);
    procedure AddMortalVessel;
    procedure RemoveMortalVessel;
    procedure FullfillConceptsComboBox;
    procedure FullfillSuperiorsComboBox(comboBox: TComboBox);
    procedure FullfillCelestialKindComboBox;
    procedure FullfillChoirAttunementsComboBox;
    procedure FullfillServitorAttunementsComboBox;
    procedure FullfillAvailableSongsComboBox;
    procedure FullfillSkillsAvailableComboBox;
    procedure FullfillServantClassComboBox;
    procedure RefreshForcePointsSpent;
    procedure RefreshCharacteristicPointsSpent;
    procedure RefreshBodyHits;
    procedure RefreshMindHits;
    procedure RefreshSoulHits;
    procedure RefreshResourcePointsSpent;
    procedure RefreshYourAttunementsList;
    procedure RefreshYourSongsList;
    procedure RefreshYourSkillsList;
    procedure RefreshYourVesselsList;
    procedure RefreshYourServantsList;
  end;

var
  frmCharacterCreation: TfrmCharacterCreation;
  character: TInNomineCharacter;

implementation

uses
  Math, uInNomineSuperiors;

{$R *.dfm}

procedure TfrmCharacterCreation.FormCreate(Sender: TObject);
begin
  character := TInNomineCharacter.Create;
end;

procedure TfrmCharacterCreation.FullfillConceptsComboBox;
var
  i: integer;
begin
  cbCharacterConcept.Clear;
  for i := 0 to NUMBER_OF_CHARACTER_CONCEPTS - 1 do
  begin
    cbCharacterConcept.Items.Add(CHARACTER_CONCEPTS_DESCRIPTORS[i]);
    if (Assigned(character.concept)) and (i = Ord(character.concept^)) then
      cbCharacterConcept.ItemIndex := i;
  end;
end;

procedure TfrmCharacterCreation.FullfillSuperiorsComboBox(comboBox: TComboBox);

  procedure FullfillSuperiorsComboBoxA(comboBox: TComboBox; a, b: integer; arrayOfDescriptions: array of String);
  var
    i: integer;
  begin
    for i := a to b - 1 do
    begin
      comboBox.Items.Add(arrayOfDescriptions[i]);
    end;
  end;

var
  oldSelectedIndex: integer;
begin
  if Assigned(character.superior) then
    oldSelectedIndex := comboBox.ItemIndex
  else
    oldSelectedIndex := -1;

  comboBox.Clear;

  if Assigned(character.concept) then
  begin
    if (character.concept^ = angel) or (character.concept^ = outcast) or ((character.concept^ = remnant) and (IsAngelic(character.celestialKind^))) then
      FullfillSuperiorsComboBoxA(comboBox, 0, NUMBER_OF_ARCHANGELS, FAMOUS_SUPERIORS_NAMES)
    else if (character.concept^ = demon) or (character.concept^ = renegade) or (character.concept^ = remnant) then
      FullfillSuperiorsComboBoxA(comboBox, NUMBER_OF_ARCHANGELS, NUMBER_OF_ARCHANGELS + NUMBER_OF_DEMON_PRINCES, FAMOUS_SUPERIORS_NAMES)
  end;

  comboBox.ItemIndex := oldSelectedIndex;
end;

procedure TfrmCharacterCreation.cbCharacterConceptSelect(Sender: TObject);
begin
  if Assigned(character.concept) then
  begin
    SysFreeMem(character.concept);
    character.concept := nil;
  end;

  character.resources.Clear;

  character.concept := AllocMem(SizeOf(TCharacterConcept));
  character.concept^ := TCharacterConcept(cbCharacterConcept.ItemIndex);
  case character.concept^ of
    angel,
    demon,
    outcast,
    renegade:
    begin
      lbKind.Visible := True;
      cbCelestialKind.Visible := True;
      lbWord.Visible := True;
      edWord.Visible := True;
      lbSuperior.Visible := True;
      cbSuperior.Visible := True;
      lbMortalSuperior.Visible := False;
      edMortalSuperior.Visible := False;
      tsAttunements.TabVisible := True;
      tsSongs.TabVisible := True;
      tsArtifacts.TabVisible := True;

      RemoveMortalVessel;
    end;
    remnant:
    begin
      lbKind.Visible := True;
      cbCelestialKind.Visible := True;
      lbWord.Visible := False;
      edWord.Visible := False;
      lbSuperior.Visible := True;
      cbSuperior.Visible := True;
      lbMortalSuperior.Visible := False;
      edMortalSuperior.Visible := False;
      tsAttunements.TabVisible := True;
      tsSongs.TabVisible := True;
      tsArtifacts.TabVisible := True;

      RemoveMortalVessel;
    end;
    soldierGod,
    soldierHell,
    undead,
    mundane:
    begin
      lbKind.Visible := False;
      cbCelestialKind.Visible := False;
      lbWord.Visible := False;
      edWord.Visible := False;
      lbSuperior.Visible := False;
      cbSuperior.Visible := False;
      lbMortalSuperior.Visible := True;
      edMortalSuperior.Visible := True;
      tsAttunements.TabVisible := False;

      if character.concept^ = mundane then
      begin
        tsSongs.TabVisible := False;
        tsArtifacts.TabVisible := False;
      end
      else
      begin
        tsSongs.TabVisible := True;
        tsArtifacts.TabVisible := True;
      end;

      AddMortalVessel;
    end;
  end;

  // clearing
  edName.Clear;
  edWord.Clear;
  edMortalSuperior.Clear;

  if Assigned(character.celestialKind) then
  begin
    SysFreeMem(character.celestialKind);
    character.celestialKind := nil;
  end;

  character.superior := nil;
  cbSuperior.Clear;
  FullfillCelestialKindComboBox;
end;

procedure TfrmCharacterCreation.FullfillCelestialKindComboBox;

  procedure FullfillCelestialKindComboBoxA(a, b: integer; arrayOfDescriptions: array of String);
  var
    i: integer;
  begin
    for i := a to b - 1 do
    begin
      cbCelestialKind.Items.Add(arrayOfDescriptions[i]);
    end;
  end;

var
  oldSelectedIndex: integer;
begin
  if Assigned(character.celestialKind) then
    oldSelectedIndex := cbCelestialKind.ItemIndex
  else
    oldSelectedIndex := -1;
  
  cbCelestialKind.Clear;

  if Assigned(character.concept) then
  begin
    if (character.concept^ = angel) or (character.concept^ = outcast) then
      FullfillCelestialKindComboBoxA(0, NUMBER_OF_ANGEL_CHOIRS, CELESTIAL_KINDS_DESCRIPTORS)
    else if (character.concept^ = demon) or (character.concept^ = renegade) then
      FullfillCelestialKindComboBoxA(NUMBER_OF_ANGEL_CHOIRS, NUMBER_OF_ANGEL_CHOIRS + NUMBER_OF_DEMON_BANDS, CELESTIAL_KINDS_DESCRIPTORS)
    else if character.concept^ = remnant then
      FullfillCelestialKindComboBoxA(0, NUMBER_OF_ANGEL_CHOIRS + NUMBER_OF_DEMON_BANDS, CELESTIAL_KINDS_DESCRIPTORS);
  end;

  cbCelestialKind.ItemIndex := oldSelectedIndex;
end;

procedure TfrmCharacterCreation.btNextClick(Sender: TObject);
begin
  pcCharacterCreationSteps.SelectNextPage(True);
end;

procedure TfrmCharacterCreation.cbCelestialKindChange(Sender: TObject);
begin
  if assigned(character.celestialKind) then
  begin
    SysFreeMem(character.celestialKind);
    character.celestialKind := nil;
  end;

  character.celestialKind := AllocMem(SizeOf(TCelestialKind));

  if Assigned(character.concept) then
    if (character.concept^ = demon) or (character.concept^ = renegade) then
      character.celestialKind^ := TCelestialKind(cbCelestialKind.ItemIndex + NUMBER_OF_ANGEL_CHOIRS)
    else
      character.celestialKind^ := TCelestialKind(cbCelestialKind.ItemIndex);

  cbSuperior.ItemIndex := -1;
  FullfillSuperiorsComboBox(cbSuperior);
end;

procedure TfrmCharacterCreation.cbSuperiorChange(Sender: TObject);
begin
  if Assigned(character.concept) then
  begin
    if (character.concept^ = angel) or (character.concept^ = outcast) or ((character.concept^ = remnant) and (IsAngelic(character.celestialKind^))) then
      character.superior := famousSuperiors[cbSuperior.ItemIndex]
    else if (character.concept^ = demon) or (character.concept^ = renegade) or (character.concept^ = remnant) then
      character.superior := famousSuperiors[NUMBER_OF_ARCHANGELS + cbSuperior.ItemIndex]
  end;

  AddFreeChoirAttunement(character.superior);
end;

procedure TfrmCharacterCreation.edNameChange(Sender: TObject);
begin
  character.name := TEdit(Sender).Text;
end;

procedure TfrmCharacterCreation.edWordChange(Sender: TObject);
begin
  character.word := TEdit(Sender).Text;
end;

procedure TfrmCharacterCreation.tsConceptShow(Sender: TObject);
begin
  FullfillConceptsComboBox;
end;

procedure TfrmCharacterCreation.tsForcesAndCharacteristicsShow(
  Sender: TObject);
begin
  if Assigned(character.concept) then
  begin
    case character.concept^ of
      soldierGod,
      soldierHell,
      undead,
      mundane:
        lbBodyHits.Caption := 'Body Hits';
      else
        lbBodyHits.Caption := 'Base Body Hits';
    end;
  end;

  RefreshForcePointsSpent;
  RefreshCharacteristicPointsSpent;
end;

procedure TfrmCharacterCreation.RefreshForcePointsSpent;

  procedure DisableForceEdit(forceEdit: TEdit);
  begin
    forceEdit.ReadOnly := True;
    forceEdit.BorderStyle := bsNone;
    forceEdit.Color := clBtnFace;
  end;

  procedure EnableForceEdit(forceEdit: TEdit);
  begin
    forceEdit.ReadOnly := False;
    forceEdit.BorderStyle := bsSingle;
    forceEdit.Color := clWindow;
  end;

begin
  gbForces.Caption := format('Forces: %d', [GetForcesPointsToSpend]);
  edCorporealForces.Text := intToStr(character.corporealForces.number);
  edEtherealForces.Text := intToStr(character.etherealForces.number);
  edCelestialForces.Text := intToStr(character.celestialForces.number);

  if TRealm(rgForces.ItemIndex) = corporeal then
  begin
    EnableForceEdit(edCorporealForces);
    DisableForceEdit(edEtherealForces);
    DisableForceEdit(edCelestialForces);
  end
  else if TRealm(rgForces.ItemIndex) = ethereal then
  begin
    DisableForceEdit(edCorporealForces);
    EnableForceEdit(edEtherealForces);
    DisableForceEdit(edCelestialForces);
  end
  else if TRealm(rgForces.ItemIndex) = celestial then
  begin
    DisableForceEdit(edCorporealForces);
    DisableForceEdit(edEtherealForces);
    EnableForceEdit(edCelestialForces);
  end;
end;

procedure TfrmCharacterCreation.RefreshCharacteristicPointsSpent;
begin
  gbCharacteristics.Caption := format('Characteristics: %d', [GetCharacteristicsPointsToSpend(TForcesType(rgForces.ItemIndex))]);

  lbAttribute1.Caption := CHARACTERISTICS_DESCRIPTORS[rgForces.ItemIndex];
  lbAttribute2.Caption := CHARACTERISTICS_DESCRIPTORS[rgForces.ItemIndex + 3];

  edAttribute1.Text := intToStr(character.forces[rgForces.ItemIndex].attribute[0]);
  edAttribute2.Text := intToStr(character.forces[rgForces.ItemIndex].attribute[1]);
end;

procedure TfrmCharacterCreation.rgForcesClick(Sender: TObject);
begin
  RefreshForcePointsSpent;
  RefreshCharacteristicPointsSpent;
end;

procedure TfrmCharacterCreation.edForcesChange(Sender: TObject);
var
  newForces: integer;
begin
  if Assigned(character.concept) then
  begin
    newForces := strToIntDef(TEdit(Sender).Text, 0);

    character.forces[rgForces.ItemIndex].number := newForces;

    if (rgForces.ItemIndex = Ord(corporeal)) then
      case character.concept^ of
        soldierGod,
        soldierHell,
        undead,
        mundane:
          GetMortalVessel.level := character.corporealForces.number;
      end;    

    RefreshForcePointsSpent;
    RefreshCharacteristicPointsSpent;
  end;
end;

function TfrmCharacterCreation.GetForcesPointsToSpend: integer;
begin
  if assigned(character.concept) then
    Result := GetTotalForcesPointsToSpend - character.numberForces
  else
    Result := 0;
end;

function TfrmCharacterCreation.GetCharacteristicsPointsToSpend(forcesType: TForcesType): integer;
begin
  Result := GetTotalCharacteristicsPointsToSpend(forcesType) -
            (character.forces[rgForces.ItemIndex].attribute[0] +
             character.forces[rgForces.ItemIndex].attribute[1]);
end;

procedure TfrmCharacterCreation.edAttributeChange(Sender: TObject);
var
  newAttribute: integer;
  attributeNumber: integer;
begin
  newAttribute := strToIntDef(TEdit(Sender).Text, 0);
  attributeNumber := strToInt(Copy(TEdit(Sender).Name, Length(TEdit(Sender).Name), 1)) - 1;

  if (character.forces[rgForces.ItemIndex].number > 0) and (GetCharacteristicsPointsToSpend(TForcesType(rgForces.ItemIndex)) + (character.forces[rgForces.ItemIndex].attribute[attributeNumber] - newAttribute) < -3) then
  begin
    character.forces[rgForces.ItemIndex].number := character.forces[rgForces.ItemIndex].number + Floor((GetTotalCharacteristicsPointsToSpend(TForcesType(rgForces.ItemIndex)) + (character.forces[rgForces.ItemIndex].attribute[attributeNumber] - newAttribute)) / 4);
    RefreshForcePointsSpent;  
  end;

  if  (character.forces[rgForces.ItemIndex].attribute[attributeNumber] <> newAttribute) or
      (character.forces[rgForces.ItemIndex].attribute[attributeNumber] = 0) then
  begin
    character.forces[rgForces.ItemIndex].attribute[attributeNumber] := newAttribute;

    if attributeNumber = 0 then
    begin
      case TRealm(rgForces.ItemIndex) of
        corporeal:
          RefreshBodyHits;
        ethereal:
          RefreshMindHits;
        celestial:
          RefreshSoulHits;
      end;
    end;

    RefreshCharacteristicPointsSpent;
  end;
end;

function TfrmCharacterCreation.GetTotalForcesPointsToSpend: integer;
begin
  Result := CHARACTER_CONCEPTS_FORCE_POINTS[Ord(character.concept^)];
end;

function TfrmCharacterCreation.GetTotalCharacteristicsPointsToSpend(forcesType: TForcesType): integer;
begin
  Result := (character.forces[rgForces.ItemIndex].number * 4);
end;

procedure TfrmCharacterCreation.RefreshBodyHits;
begin
  if Assigned(character.concept) then
    case character.concept^ of
      soldierGod,
      soldierHell,
      undead,
      mundane:
        edBodyHits.Text := intToStr(GetMortalVessel.level * character.corporealForces.number * TCorporealForces(character.corporealForces).strength);
      else
        edBodyHits.Text := intToStr(character.corporealForces.number * TCorporealForces(character.corporealForces).strength);
    end;
end;

procedure TfrmCharacterCreation.RefreshMindHits;
begin
  edMindHits.Text := intToStr(character.etherealForces.number * TEtherealForces(character.etherealForces).intelligence);
end;

procedure TfrmCharacterCreation.RefreshSoulHits;
begin
  edSoulHits.Text := intToStr(character.celestialForces.number * TCelestialForces(character.celestialForces).will);
end;

procedure TfrmCharacterCreation.tsAttunementsShow(Sender: TObject);
begin
  RefreshResourcePointsSpent;
  FullfillChoirAttunementsComboBox;
  RefreshYourAttunementsList;
  FullfillSuperiorsComboBox(cbSuperior1);
end;

procedure TfrmCharacterCreation.FullfillChoirAttunementsComboBox;
var
  i: integer;
begin
  cbChoirAttunements.Clear;

  if Assigned(character.superior) then
  begin
    for i := 0 to character.superior.choirAttunements.Count - 1 do
      cbChoirAttunements.Items.Add(TAttunement(character.superior.choirAttunements.Items[i]).description);
  end;
end;

procedure TfrmCharacterCreation.RefreshYourAttunementsList;
var
  i: integer;
begin
  lbAttunements.Clear;

  for i := 0 to character.resources.Count - 1 do
    if character.resources.Items[i] is TAttunement then
      lbAttunements.Items.Add(TAttunement(character.resources.Items[i]).description);
end;

procedure TfrmCharacterCreation.pcCharacterCreationStepsChanging(
  Sender: TObject; var AllowChange: Boolean);
begin
  case pcCharacterCreationSteps.ActivePageIndex of
    0: // concept page
    begin
      if not assigned(character.concept) then
      begin
        MessageDlg('You need to choose a character concept!', mtWarning, [mbOK], 0);
        AllowChange := False;
        Exit;
      end;

      case character.concept^ of
        angel,
        demon,
        outcast,
        renegade,
        remnant:
        begin
          if not Assigned(character.celestialKind) then
          begin
            MessageDlg('You need to choose a character celestial kind!', mtWarning, [mbOK], 0);
            AllowChange := False;
            Exit;
          end;

          if not Assigned(character.superior) then
          begin
            MessageDlg('You need to choose a superior!', mtWarning, [mbOK], 0);
            AllowChange := False;
            Exit;
          end;
        end;
      end;
    end;
    1:  // forces and characteristics
    begin
      if character.numberForces < GetTotalForcesPointsToSpend then
      begin
        if MessageDlg('You didn''t spend all your force points. Do you want to proceed anyway?', mtInformation, [mbYes, mbNo], 0) = mrNo then
        begin
          //TPageControl(Sender).SelectNextPage(False);
          AllowChange := False;
          Exit;
        end;
      end;

      if (GetCharacteristicsPointsToSpend(corporeal) > 0) or
         (GetCharacteristicsPointsToSpend(ethereal) > 0) or
         (GetCharacteristicsPointsToSpend(celestial) > 0) then
      begin
        if MessageDlg('You didn''t spend all your characteristics points. Do you want to proceed anyway?', mtInformation, [mbYes, mbNo], 0) = mrNo then
        begin
          //TPageControl(Sender).SelectNextPage(False);
          AllowChange := False;
          Exit;
        end;
      end;

      character.resourcePoints := character.numberForces * 4;
    end;
    2:; // attunements page
    3:; // skills page
    4:; // artifacts page
  end;
end;

procedure TfrmCharacterCreation.cbSuperior1Change(Sender: TObject);
begin
  FullfillServitorAttunementsComboBox;
end;

procedure TfrmCharacterCreation.FullfillServitorAttunementsComboBox;

  procedure FullfillServitorAttunementsComboBoxA(superiorIndex: integer);
  var
    superior: TSuperior;
    i: integer;
  begin
    superior := famousSuperiors[superiorIndex];

    for i := 0 to superior.servitorAttunements.Count - 1 do
      cbServitorAttunements.Items.Add(TAttunement(superior.servitorAttunements.Items[i]).description);
  end;

begin
  cbServitorAttunements.Clear;

  if Assigned(character.concept) then
  begin
    if (character.concept^ = angel) or (character.concept^ = outcast) or ((character.concept^ = remnant) and (IsAngelic(character.celestialKind^))) then
      FullfillServitorAttunementsComboBoxA(cbSuperior1.ItemIndex)
    else if (character.concept^ = demon) or (character.concept^ = renegade) or (character.concept^ = remnant) then
      FullfillServitorAttunementsComboBoxA(cbSuperior1.ItemIndex + NUMBER_OF_ARCHANGELS);
  end;
end;

procedure TfrmCharacterCreation.RefreshResourcePointsSpent;
var
  caption: String;
begin
  caption := format('Resources: %d', [GetResoucePointsToSpend]);
  gbAttunements.Caption := caption;
  gbSongs.Caption := caption;
  gbSkills.Caption := caption;
  gbVesselsAndServants.Caption := caption;
end;

function TfrmCharacterCreation.GetResoucePointsToSpend: integer;
var
  i: integer;
begin
  Result := character.resourcePoints;

  for i := 0 to character.resources.Count - 1 do
    Result := Result - TResource(character.resources.Items[i]).cost;
end;

procedure TfrmCharacterCreation.AddFreeChoirAttunement(
  superior: TSuperior);
begin
  character.resources.Clear;

  if (Assigned(character.celestialKind)) and (IsAngelic(character.celestialKind^)) then
    character.resources.Add( superior.choirAttunements.Items[Ord(character.celestialKind^)] )
  else if Assigned(character.celestialKind) then
    character.resources.Add( superior.choirAttunements.Items[Ord(character.celestialKind^) - NUMBER_OF_ANGEL_CHOIRS] );
end;

procedure TfrmCharacterCreation.btAddChoirAttunementClick(Sender: TObject);
var
  attunement: TAttunement;
begin
  if (Assigned(character.celestialKind)) and (Assigned(character.superior)) and (cbChoirAttunements.ItemIndex > -1) then
  begin
    if IsAngelic(character.celestialKind^) then
      attunement := TAttunement(character.superior.choirAttunements.Items[cbChoirAttunements.ItemIndex])
    else
      attunement := TAttunement(character.superior.choirAttunements.Items[cbChoirAttunements.ItemIndex]);

    if HasThatAttunement(attunement) then
    begin
      MessageDlg('You already have this choir attunement.', mtWarning, [mbOk], 0);
    end
    else
    begin
      attunement := TAttunement.Clone(attunement);
      attunement.cost := 5;
      character.resources.Add(attunement);

      RefreshResourcePointsSpent;
      RefreshYourAttunementsList;
    end;
  end;
end;

procedure TfrmCharacterCreation.btAddServitorAttunementClick(
  Sender: TObject);

  function GetServitorAttunement(servitorAttunementDescription: String): TAttunement;
  var
    i, j: integer;
  begin
    for i := 0 to NUMBER_OF_ARCHANGELS + NUMBER_OF_DEMON_PRINCES - 1 do
      for j := 0 to famousSuperiors[i].servitorAttunements.Count - 1 do
        if servitorAttunementDescription = TAttunement(famousSuperiors[i].servitorAttunements.Items[j]).description then
        begin
          Result := TAttunement(famousSuperiors[i].servitorAttunements.Items[j]);
          Exit;
        end;

    Result := nil;
  end;

var
  attunement: TAttunement;
begin
  if (Assigned(character.celestialKind)) and (cbSuperior1.ItemIndex > -1) then
  begin
    attunement := GetServitorAttunement(cbServitorAttunements.Items.Strings[cbServitorAttunements.ItemIndex]);

    if Assigned(attunement) then
    begin
      if HasThatAttunement(attunement) then
      begin
        MessageDlg('You already have this servitor attunement.', mtWarning, [mbOk], 0);
      end
      else
      begin
        attunement := TAttunement.Clone(attunement);
        attunement.cost := 10;
        character.resources.Add(attunement);

        RefreshResourcePointsSpent;
        RefreshYourAttunementsList;
      end;
    end;
  end;
end;

function TfrmCharacterCreation.HasThatAttunement(attunement: TAttunement): boolean;
var
  i: integer;
begin
  for i := 0 to character.resources.Count - 1 do
    if character.resources.Items[i] is TAttunement then
      if attunement.description = TAttunement(character.resources.Items[i]).description then
      begin
        Result := True;
        Exit;
      end;

  Result := False;
end;


procedure TfrmCharacterCreation.btRemoveAttunementClick(Sender: TObject);
var
  attunementDescription: String;
  i: integer;
begin
  if lbAttunements.ItemIndex > -1 then
  begin
    attunementDescription := lbAttunements.Items.Strings[lbAttunements.ItemIndex];

    for i := 0 to character.resources.Count - 1 do
    begin
      if character.resources.Items[i] is TAttunement then
        if attunementDescription = TAttunement(character.resources.Items[i]).description then
        begin
          if TAttunement(character.resources.Items[i]).cost > 0 then
          begin
            character.resources.Remove(character.resources.Items[i]);
            RefreshResourcePointsSpent;
            RefreshYourAttunementsList;
            Exit;
          end;
        end;
    end;
  end;
end;

procedure TfrmCharacterCreation.FullfillAvailableSongsComboBox;
var
  i: integer;
begin
  cbAvailableSongs.Clear;

  for i := 0 to Length(songs) - 1 do
    if rgSongRealm.ItemIndex = Ord(TSong(songs[i]).realm) then
      cbAvailableSongs.Items.Add( format('%s Song of %s', [REALM_DESCRIPTORS[Ord(TSong(songs[i]).realm)], TSong(songs[i]).description]) );
end;

procedure TfrmCharacterCreation.tsSongsShow(Sender: TObject);
begin
  RefreshResourcePointsSpent;
  FullfillAvailableSongsComboBox;
  RefreshYourSongsList;
end;

procedure TfrmCharacterCreation.rgSongRealmClick(Sender: TObject);
begin
  edSongLevel.Clear;
  FullfillAvailableSongsComboBox;
end;

procedure TfrmCharacterCreation.RefreshYourSongsList;
var
  i: integer;
begin
  lbSongs.Clear;

  for i := 0 to character.resources.Count - 1 do
    if character.resources.Items[i] is TSong then
      lbSongs.Items.Add( format('%s Song of %s - Level: %d', [REALM_DESCRIPTORS[Ord(TSong(character.resources.Items[i]).realm)], TSong(character.resources.Items[i]).description, TSong(character.resources.Items[i]).level]) );
end;

procedure TfrmCharacterCreation.btSongBuyClick(Sender: TObject);

  function GetSong(songDescription: String; realm: TRealm): TSong;
  var
    i: integer;
  begin
    for i := 0 to Length(songs) - 1 do
      if (songs[i].description = songDescription) and (songs[i].realm = realm) then
      begin
        Result := songs[i];
        Exit;
      end;

    Result := nil;
  end;

var
  song: TSong;
  songLevel: integer;
begin
  if (cbAvailableSongs.ItemIndex > -1) and (rgSongRealm.ItemIndex > -1) then
  begin
    songLevel := strToIntDef(edSongLevel.Text, 0);

    if songLevel <= 0 then
    begin
      MessageDlg('The song level must be greater than 0.', mtWarning, [mbOk], 0);
      Exit;
    end;

    song := GetSong(SONGS_NAMES[cbAvailableSongs.ItemIndex], TRealm(rgSongRealm.ItemIndex));

    if Assigned(song) then
    begin
      if HasThatSong(song) then
      begin
        song := GetSongFromYourResources(song.description, song.realm);
        song.cost := songLevel;
        song.level := songLevel;
      end
      else
      begin
        song := TSong.Clone(song);
        song.cost := songLevel;
        song.level := songLevel;
        character.resources.Add(song);
      end;

      RefreshResourcePointsSpent;
      RefreshYourSongsList;
    end;
  end;
end;

function TfrmCharacterCreation.HasThatSong(song: TSong): boolean;
var
  i: integer;
begin
  for i := 0 to character.resources.Count - 1 do
    if character.resources.Items[i] is TSong then
      if (song.description = TSong(character.resources.Items[i]).description) and (song.realm = TSong(character.resources.Items[i]).realm) then
      begin
        Result := True;
        Exit;
      end;

  Result := False;
end;

procedure TfrmCharacterCreation.btSongRemoveClick(Sender: TObject);
var
  song: TSong;
  selectedSongDescription: String;
  songDescription: String;
  songRealm: TRealm;
  i: integer;
begin
  if (lbSongs.ItemIndex > -1) then
  begin
    selectedSongDescription := lbSongs.Items.Strings[lbSongs.ItemIndex];

    for i := 0 to NUMBER_OF_SONGS - 1 do
    begin
      if Pos(SONGS_NAMES[i], selectedSongDescription) > 0 then
      begin
        songDescription := SONGS_NAMES[i];
        Break;
      end;
    end;

    if Pos(REALM_DESCRIPTORS[Ord(corporeal)], selectedSongDescription) > 0 then
      songRealm := corporeal
    else if Pos(REALM_DESCRIPTORS[Ord(ethereal)], selectedSongDescription) > 0 then
      songRealm := ethereal
    else
      songRealm := celestial;

    song := GetSongFromYourResources(songDescription, songRealm);

    if Assigned(song) then
    begin
      character.resources.Remove(song);
      RefreshResourcePointsSpent;
      RefreshYourSongsList;
    end;
  end;
end;

function TfrmCharacterCreation.GetSongFromYourResources(
  songDescription: String; songRealm: TRealm): TSong;
var
  i: integer;
begin
  for i := 0 to character.resources.Count - 1 do
    if character.resources.Items[i] is TSong then
      if (TSong(character.resources.Items[i]).description = songDescription) and (TSong(character.resources.Items[i]).realm = songRealm) then
      begin
        Result := TSong(character.resources.Items[i]);
        Exit;
      end;

  Result := nil;
end;

procedure TfrmCharacterCreation.tsSkillsShow(Sender: TObject);
begin
  RefreshResourcePointsSpent;
  FullfillSkillsAvailableComboBox;
end;

procedure TfrmCharacterCreation.FullfillSkillsAvailableComboBox;
var
  i: integer;
begin
  cbAvailableSkills.Clear;

  for i := 0 to NUMBER_OF_SKILLS - 1 do
    cbAvailableSkills.Items.Add(skills[i].description);
end;

procedure TfrmCharacterCreation.cbAvailableSkillsChange(Sender: TObject);

  function GetAttributesFromSkillString(skill: TSkill): String;
  var
    i: integer;
  begin
    Result := '';
    for i := 0 to NUMBER_OF_CHARACTERISTICS - 1 do
    begin
      if TCharacteristicType(i) in skill.attributes then
        Result := Result + format(' or %s', [CHARACTERISTICS_DESCRIPTORS[i]]);
    end;

    if Length(Result) > 3 then
    Result := Copy(Result, 4, Length(Result) - 3);
  end;

var
  skill: TSkill;
begin
  skill := skills[cbAvailableSkills.ItemIndex];
  if skill.needsSpecification then
  begin
    lbSkillSpecification.Visible := True;
    edSkillSpecification.Visible := True;
  end
  else
  begin
    lbSkillSpecification.Visible := False;
    edSkillSpecification.Visible := False;
  end;

  lbSkillAttribute2.Caption := GetAttributesFromSkillString(skill);
  lbSkillDefault2.Caption := intToStr(skill.defaultPenalty);
  edSkillSpecification.Clear;
  edSkillLevel.Clear;
end;

procedure TfrmCharacterCreation.btSkillBuyClick(Sender: TObject);
var
  skill: TSkill;
  skillLevel: integer;
  skillSpecification: String;
begin
  if (cbAvailableSkills.ItemIndex > -1) then
  begin
    skill := skills[cbAvailableSkills.ItemIndex];
    skillLevel := strToIntDef(edSkillLevel.Text, 0);
    skillSpecification := Trim(edSkillSpecification.Text);

    if skillLevel <= 0 then
    begin
      MessageDlg('The skill level must be greater than 0.', mtWarning, [mbOk], 0);
      Exit;
    end;

    if (skill.needsSpecification) and (skillSpecification = '') then
    begin
      MessageDlg('You must inform a skill specification.', mtWarning, [mbOk], 0);
      Exit;
    end;

    if HasThatSkill(skill) then
    begin
      skill := GetSkillFromYourResources(skill.description, skill.specification);
      skill.cost := skillLevel;
      skill.level := skillLevel;
      skill.specification := skillSpecification;
    end
    else
    begin
      skill := TSkill.Clone(skill);
      skill.cost := skillLevel;
      skill.level := skillLevel;
      skill.specification := skillSpecification;
      character.resources.Add(skill);
    end;

    RefreshResourcePointsSpent;
    RefreshYourSkillsList;
  end;
end;

function TfrmCharacterCreation.HasThatSkill(skill: TSkill): boolean;
var
  i: integer;
begin
  for i := 0 to character.resources.Count - 1 do
    if character.resources.Items[i] is TSkill then
      if (skill.description = TSkill(character.resources.Items[i]).description) and (skill.specification = TSkill(character.resources.Items[i]).specification) then
      begin
        Result := True;
        Exit;
      end;

  Result := False;
end;

procedure TfrmCharacterCreation.RefreshYourSkillsList;
var
  i: integer;
begin
  lbSkills.Clear;

  for i := 0 to character.resources.Count - 1 do
    if character.resources.Items[i] is TSkill then
      if TSkill(character.resources.Items[i]).needsSpecification then
        lbSkills.Items.Add( format('%s (%s) - Level: %d', [TSkill(character.resources.Items[i]).description, TSkill(character.resources.Items[i]).specification, TSkill(character.resources.Items[i]).level]) )
      else
        lbSkills.Items.Add( format('%s - Level: %d', [TSkill(character.resources.Items[i]).description, TSkill(character.resources.Items[i]).level]) );
end;

function TfrmCharacterCreation.GetSkillFromYourResources(
  skillDescription: String; skillSpecification: String): TSkill;
var
  i: integer;
begin
  for i := 0 to character.resources.Count - 1 do
    if character.resources.Items[i] is TSkill then
      if (TSong(character.resources.Items[i]).description = skillDescription) and (TSkill(character.resources.Items[i]).specification = skillSpecification) then
      begin
        Result := TSkill(character.resources.Items[i]);
        Exit;
      end;

  Result := nil;
end;

procedure TfrmCharacterCreation.cbAvailableSongsChange(Sender: TObject);
begin
  edSongLevel.Clear;
end;

procedure TfrmCharacterCreation.btSkillRemoveClick(Sender: TObject);
var
  skillDescription: String;
  skillSpecification: String;
  tempPosition1: integer;
  tempPosition2: integer;
  skill: TSkill;
begin
  if (lbSkills.ItemIndex > -1) then
  begin
    skillDescription := lbSkills.Items.Strings[lbSkills.ItemIndex];

    if Pos('(', skillDescription) > 0 then
    begin
      tempPosition1 := Pos('(', skillDescription);
      tempPosition2 := Pos(')', skillDescription);
      skillSpecification := Copy(skillDescription, tempPosition1 + 1, tempPosition2 - tempPosition1 - 1);
      skillDescription := Copy(skillDescription, 1, tempPosition1 - 2);
      skill := GetSkillFromYourResources(skillDescription, skillSpecification);
    end
    else
    begin
      tempPosition1 := Pos('-', skillDescription);
      skillDescription := Copy(skillDescription, 1, tempPosition1 - 2);
      skill := GetSkillFromYourResources(skillDescription);
    end;

    if Assigned(skill) then
    begin
      character.resources.Remove(skill);
      RefreshResourcePointsSpent;
      RefreshYourSkillsList;
    end;
  end;
end;

procedure TfrmCharacterCreation.tbVesselAndServantShow(Sender: TObject);
begin
  RefreshResourcePointsSpent;
  rgVesselOrServant.ItemIndex := 0;
  FullfillServantClassComboBox;  
  RefreshYourVesselsList;
  RefreshYourServantsList;
end;

procedure TfrmCharacterCreation.rgVesselOrServantClick(Sender: TObject);
begin
  if Assigned(character.concept) then
  begin
    edVesselOrServantDescription.Clear;
    edVesselOrServantLevel.Clear;
    edVesselCharismaLevel.Clear;
    edVesselRoleLevel.Clear;
    edVesselStatus.Clear;
    cbServantClass.ItemIndex := -1;

    if rgVesselOrServant.ItemIndex = 0 then
    begin
      lbVesselCharismaLevel.Visible := True;
      edVesselCharismaLevel.Visible := True;
      lbVesselRoleLevel.Visible := True;
      edVesselRoleLevel.Visible := True;
      lbVesselStatus.Visible := True;
      edVesselStatus.Visible := True;
      lbServantClass.Visible := False;
      cbServantClass.Visible := False;

      case character.concept^ of
        soldierGod,
        soldierHell,
        undead,
        mundane:
        begin
          edVesselOrServantDescription.Text := 'Mortal Vessel';
          edVesselOrServantDescription.ReadOnly := True;
          lbVesselOrServantLevel.Visible := False;
          edVesselOrServantLevel.Visible := False;
          lbVesselRoleLevel.Visible := False;
          edVesselRoleLevel.Visible := False;

          btVesselOrServantBuy.Caption := 'Alter Vessel';
          btVesselOrServantRemove.Visible := False;
        end;
        else
        begin
          edVesselOrServantDescription.ReadOnly := False;
          lbVesselOrServantLevel.Visible := True;
          edVesselOrServantLevel.Visible := True;
          lbVesselRoleLevel.Visible := True;
          edVesselRoleLevel.Visible := True;

          btVesselOrServantBuy.Caption := 'Buy Vessel';
          btVesselOrServantRemove.Visible := True;
          btVesselOrServantRemove.Caption := 'Remove Vessel';
        end;
      end;
    end
    else
    begin
      edVesselOrServantDescription.ReadOnly := False;
      lbVesselOrServantLevel.Visible := True;
      edVesselOrServantLevel.Visible := True;
      lbVesselCharismaLevel.Visible := False;
      edVesselCharismaLevel.Visible := False;
      lbVesselRoleLevel.Visible := False;
      edVesselRoleLevel.Visible := False;
      lbVesselStatus.Visible := False;
      edVesselStatus.Visible := False;
      lbServantClass.Visible := True;
      cbServantClass.Visible := True;

      btVesselOrServantBuy.Caption := 'Buy Servant';
      btVesselOrServantRemove.Visible := True;
      btVesselOrServantRemove.Caption := 'Remove Servant';
    end;
  end;
end;

procedure TfrmCharacterCreation.RefreshYourServantsList;
var
  i: integer;
begin
  lbServants.Clear;

  for i := 0 to character.resources.Count - 1 do
    if character.resources.Items[i] is TServant then
      lbServants.Items.Add( format('%s - Lvl: %d / Cls: %d', [TServant(character.resources.Items[i]).description, TServant(character.resources.Items[i]).level, Ord(TServant(character.resources.Items[i]).servantClass) + 1]) );
end;

procedure TfrmCharacterCreation.RefreshYourVesselsList;
var
  i: integer;
begin
  lbVessels.Clear;

  for i := 0 to character.resources.Count - 1 do
    if character.resources.Items[i] is TVessel then
      lbVessels.Items.Add( format('%s - Lvl: %d / Cha: %d', [TVessel(character.resources.Items[i]).description, TVessel(character.resources.Items[i]).level, TVessel(character.resources.Items[i]).charisma.level]) );
end;

procedure TfrmCharacterCreation.FullfillServantClassComboBox;
var
  i: integer;
begin
  cbServantClass.Clear;

  for i := 0 to NUMBER_OF_SERVANT_CLASSES - 1 do
    cbServantClass.Items.Add(SERVANT_CLASSES_DESCRIPTORS[i]);
end;

procedure TfrmCharacterCreation.btVesselOrServantBuyClick(Sender: TObject);
var
  vessel: TVessel;
  vessel2: TVessel;
  servant: TServant;
  servant2: TServant;
begin
  if Assigned(character.concept) then
  begin
    if rgVesselOrServant.ItemIndex = 0 then // vessel
    begin
      case character.concept^ of
        soldierGod,
        soldierHell,
        undead,
        mundane: // modifying the unique vessel for the mortals
        begin
          vessel := GetMortalVessel;

          // charisma
          vessel.charisma := TCharisma.Create;
          vessel.charisma.level := strToIntDef(edVesselCharismaLevel.Text, 0);

          if (vessel.charisma.level > 3) or (vessel.charisma.level < -2) then
          begin
            MessageDlg('You cannot take more than 3 and less than -2 points on charisma.', mtWarning, [mbOk], 0);
            Exit;
          end;

          vessel.charisma.cost := vessel.charisma.level * 2;

          // role
          vessel.role := TRole.Create;
          vessel.role.status := strToIntDef(edVesselStatus.Text, 0);
          vessel.role.cost := vessel.role.status * 2;

          // calculating the cost
          vessel.cost := vessel.charisma.cost + vessel.role.cost;

          // negative points taken on vessels should be spent only to improve these vessels
          if vessel.cost < 0 then
            vessel.cost := 0;

          RefreshResourcePointsSpent;
          RefreshYourVesselsList;
        end;
        else // adding a new vessel for celestials
        begin
          vessel := TVessel.Create;
          vessel.description := edVesselOrServantDescription.Text;
          vessel.level := strToIntDef(edVesselOrServantLevel.Text, 0);

          if vessel.level < 1 then
          begin
            MessageDlg('You must take at least 1 level on each vessel.', mtWarning, [mbOk], 0);
            Exit;
          end;

          // charisma
          vessel.charisma := TCharisma.Create;
          vessel.charisma.level := strToIntDef(edVesselCharismaLevel.Text, 0);

          if (vessel.charisma.level > 3) or (vessel.charisma.level < -2) then
          begin
            MessageDlg('You cannot take more than 3 and less than -2 points on charisma.', mtWarning, [mbOk], 0);
            Exit;
          end;

          vessel.charisma.cost := vessel.charisma.level * 2;

          // role
          vessel.role := TRole.Create;
          vessel.role.level := strToIntDef(edVesselRoleLevel.Text, 0);

          if vessel.role.level < 0 then
          begin
            MessageDlg('You cannot take a role level lesser than 0.', mtWarning, [mbOk], 0);
            Exit;
          end;

          vessel.role.status := strToIntDef(edVesselStatus.Text, 0);

          if vessel.role.status > vessel.role.level then
          begin
            MessageDlg('You cannot take a status greater than your role level.', mtWarning, [mbOk], 0);
            Exit;
          end;

          vessel.role.cost := Ceil(vessel.role.status * vessel.role.level / 2);

          // calculating the cost
          vessel.cost := (vessel.level * 3) + vessel.charisma.cost + vessel.role.cost;

          // negative points taken on vessels should be spent only to improve these vessels
          if vessel.cost < 0 then
            vessel.cost := 0;

          if HasThatVessel(vessel) then
          begin
            //MessageDlg('Each vessel''s description must be unique.', mtWarning, [mbOk], 0);
            vessel2 := GetVesselFromYourResources(vessel.description);
            vessel2.level := vessel.level;
            FreeAndNil(vessel2.charisma);
            vessel2.charisma := vessel.charisma;
            FreeAndNil(vessel2.role);
            vessel2.role := vessel.role;
            vessel2.cost := vessel.cost;
          end
          else
            character.resources.Add(vessel);

          RefreshResourcePointsSpent;
          RefreshYourVesselsList;
        end;
      end;
    end
    else // servant
    begin
      if cbServantClass.ItemIndex < 0 then
      begin
        MessageDlg('You must choose a servant class!', mtWarning, [mbOk], 0);
        Exit;
      end;

      servant := TServant.Create;
      servant.servantClass := TServantClass(cbServantClass.ItemIndex);
      servant.description := edVesselOrServantDescription.Text;
      servant.level := strToIntDef(edVesselOrServantLevel.Text, 0);

      if servant.level < 1 then
      begin
        MessageDlg('You must take at least 1 level on each servant.', mtWarning, [mbOk], 0);
        Exit;
      end;

      servant.cost := Ceil((cbServantClass.ItemIndex + 1) * servant.level / 2);

      if HasThatServant(servant) then
      begin
        servant2 := GetServantFromYourResources(servant.description, servant.servantClass);
        servant2.level := servant.level;
        servant2.cost := servant.cost;
      end
      else
        character.resources.Add(servant);

      RefreshResourcePointsSpent;
      RefreshYourServantsList;
    end;
  end;
end;

function TfrmCharacterCreation.GetServantFromYourResources(
  servantDescription: String; servantClass: TServantClass): TServant;
var
  i: integer;
begin
  for i := 0 to character.resources.Count - 1 do
  begin
    if character.resources.Items[i] is TServant then
      if (servantDescription = TServant(character.resources.Items[i]).description) and (servantClass = TServant(character.resources.Items[i]).servantClass) then
      begin
        Result := TServant(character.resources.Items[i]);
        Exit;
      end;
  end;

  Result := nil;
end;

function TfrmCharacterCreation.GetVesselFromYourResources(
  vesselDescription: String): TVessel;
var
  i: integer;
begin
  for i := 0 to character.resources.Count - 1 do
  begin
    if character.resources.Items[i] is TVessel then
      if vesselDescription = TVessel(character.resources.Items[i]).description then
      begin
        Result := TVessel(character.resources.Items[i]);
        Exit;
      end;
  end;

  Result := nil;
end;

function TfrmCharacterCreation.HasThatServant(servant: TServant): boolean;
var
  i: integer;
begin
  for i := 0 to character.resources.Count - 1 do
  begin
    if character.resources.Items[i] is TServant then
      if (servant.description = TServant(character.resources.Items[i]).description) and (servant.servantClass = TServant(character.resources.Items[i]).servantClass) then
      begin
        Result := True;
        Exit;
      end;
  end;

  Result := False;
end;

function TfrmCharacterCreation.HasThatVessel(vessel: TVessel): boolean;
var
  i: integer;
begin
  for i := 0 to character.resources.Count - 1 do
  begin
    if character.resources.Items[i] is TVessel then
      if vessel.description = TVessel(character.resources.Items[i]).description then
      begin
        Result := True;
        Exit;
      end;
  end;

  Result := False;
end;

procedure TfrmCharacterCreation.AddMortalVessel;
var
  vessel: TVessel;
begin
  vessel := TVessel.Create;
  vessel.description := 'Mortal Vessel';
  vessel.charisma := TCharisma.Create;
  vessel.charisma.level := 0;
  vessel.charisma.cost := 0;
  vessel.role := TRole.Create;
  vessel.role.level := 1;
  vessel.role.status := 0;
  vessel.role.cost := 0;
  vessel.cost := 0;

  character.resources.Add(vessel);
end;

function TfrmCharacterCreation.GetMortalVessel: TVessel;
var
  i: integer;
begin
  for i := 0 to character.resources.Count - 1 do
  begin
    if character.resources.Items[i] is TVessel then
      if TVessel(character.resources.Items[i]).description = 'Mortal Vessel' then
      begin
        Result := TVessel(character.resources.Items[i]);
        Exit;
      end;
  end;

  Result := nil;
end;

procedure TfrmCharacterCreation.btVesselOrServantRemoveClick(
  Sender: TObject);
var
  resourceDescription: String;
  servantClass: String;
  tempPosition: integer;
  resource: TResource;
begin
  if rgVesselOrServant.ItemIndex = 0 then // vessel
  begin

    if (lbVessels.ItemIndex > -1) then
    begin
      resourceDescription := lbVessels.Items.Strings[lbVessels.ItemIndex];

      tempPosition := Pos('-', resourceDescription);
      resourceDescription := Copy(resourceDescription, 1, tempPosition - 2);
      resource := GetVesselFromYourResources(resourceDescription);

      if Assigned(resource) then
      begin
        character.resources.Remove(resource);
        RefreshResourcePointsSpent;
        RefreshYourVesselsList;
      end;
    end;

  end
  else // servant
  begin

    if (lbServants.ItemIndex > -1) then
    begin
      resourceDescription := lbServants.Items.Strings[lbServants.ItemIndex];
      servantClass := resourceDescription;

      tempPosition := Pos('-', resourceDescription);
      resourceDescription := Copy(resourceDescription, 1, tempPosition - 2);
      tempPosition := Pos('Cls:', servantClass);
      servantClass := Copy(servantClass, tempPosition + 5, Length(servantClass) - (tempPosition + 5) + 1);

      resource := GetServantFromYourResources(resourceDescription, TServantClass(strToInt(servantClass) - 1));

      if Assigned(resource) then
      begin
        character.resources.Remove(resource);
        RefreshResourcePointsSpent;
        RefreshYourServantsList;
      end;
    end;

  end;
end;

procedure TfrmCharacterCreation.edMortalSuperiorChange(Sender: TObject);
begin
  if Assigned(character.superior) then
    FreeAndNil(character.superior);

  character.superior := TSuperior.Create('');
  character.superior.name := TEdit(Sender).Text;
end;

procedure TfrmCharacterCreation.RemoveMortalVessel;
var
  vessel: TVessel;
begin
  vessel := GetMortalVessel;
  character.resources.Remove(vessel);
end;

end.
